#ifndef _AL_HELPER_H
#define _AL_HELPER_H

#include "script/include/angelscript.h"
#include "script/include/scriptstring.h"
#include "script/include/scriptstdstring.h"

#include "script/include/scriptany.h"
#include "script/include/scriptarray.h"
#include "script/include/scriptdictionary.h"

#include "script/include/scriptmath.h"
#include "script/include/scriptfile.h"

#include "script/include/scriptbuilder.h"
#include "script/include/aswrappedcall.h"


#include "templatefun.h"

class AlHelper
{
public:
    static    void    InitALScript();
    static    void    ExecuteContext(asIScriptContext *ctx);
    static    bool    ComplieScript(const char* aScriptFile);
    static    bool    ComplieSimpleScript(const char* aScriptFile);
    static    void    Callfunction(const char* aModulename, const char* aFuncname);
    template<typename A1>
    static    void    Callfunction(const char* aModulename, const char* aFuncname, A1 a1)
    {
        asIScriptContext *ctx = CreateContext(aModulename, aFuncname);
        if (NULL != ctx)
        {
            int ArgIndex = 0;
            SetArg<A1>(ctx, ArgIndex++, a1);
            ExecuteContext(ctx);
        }
    }

    template<typename A1, typename A2>
    static    void    Callfunction(const char* aModulename, const char* aFuncname, A1 a1, A2 a2)
    {
        asIScriptContext *ctx = CreateContext(aModulename, aFuncname);
        if (NULL != ctx)
        {
            int ArgIndex = 0;
            SetArg<A1>(ctx, ArgIndex++, a1);
            SetArg<A2>(ctx, ArgIndex++, a2);
            ExecuteContext(ctx);
        }
    }

    static    bool    RegisterGlobalFunction(const char *aDeclaration, const asSFuncPtr &aFuncPointer, asDWORD aCallConv);

    template<typename ExportClass>
    static bool ExportClass()
    {
        string classname = TemplateFunc::GetBaseTypeName<BaseType<ExportClass>::BASETYPE>();
        int r = -1;

        do 
        {
            int r = sm_pEngine->RegisterObjectType(classname.c_str(), 0, asOBJ_REF);
            string factoryfuncname = classname + "@ f()";
            cout << factoryfuncname << endl;
            if(r < 0)
            {
                break;
            }
            r = sm_pEngine->RegisterObjectBehaviour(classname.c_str(), asBEHAVE_FACTORY, factoryfuncname.c_str(), asFUNCTION(AlHelper::CreateClassInstance<ExportClass>), asCALL_CDECL);
            if(r < 0)
            {
                break;
            }
            r = sm_pEngine->RegisterObjectBehaviour(classname.c_str(), asBEHAVE_ADDREF, "void f()", asMETHOD(ExportClass,AddRef), asCALL_THISCALL);
            if(r < 0)
            {
                break;
            }
            r = sm_pEngine->RegisterObjectBehaviour(classname.c_str(), asBEHAVE_RELEASE, "void f()", asMETHOD(ExportClass,Release), asCALL_THISCALL);
            if(r < 0)
            {
                break;
            }
        }while(0);

        return r >= 0;
    }

    static    bool    RegisterObjectType(const char *obj, int byteSize, asDWORD flags);
    static    bool    RegisterObjectMethod(const char *obj, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv);
    static    bool    RegisterObjectBehaviour(const char *obj, asEBehaviours behaviour, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv);
protected:
private:
    template<typename ExportClass>
    static ExportClass* CreateClassInstance()
    {
        return new ExportClass();
    }

    static    asIScriptContext *    CreateContext(const char* aModulename, const char* aFuncname);

    template<typename ArgType>
    static    void                SetArg(asIScriptContext * pCtx,int aArgIndex, ArgType& aArg);
    template<>
    static    void                SetArg<asBYTE>(asIScriptContext * pCtx,int aArgIndex, asBYTE& aArg)
    {
        pCtx->SetArgByte(aArgIndex, aArg);
    }
    template<>
    static    void                SetArg<asWORD>(asIScriptContext * pCtx,int aArgIndex, asWORD& aArg)
    {
        pCtx->SetArgWord(aArgIndex, aArg);
    }
    template<>
    static    void                SetArg<asDWORD>(asIScriptContext * pCtx,int aArgIndex, asDWORD& aArg)
    {
        pCtx->SetArgDWord(aArgIndex, aArg);
    }
    template<>
    static    void                SetArg<asQWORD>(asIScriptContext * pCtx,int aArgIndex, asQWORD& aArg)
    {
        pCtx->SetArgQWord(aArgIndex, aArg);
    }
    template<>
    static    void                SetArg<float>(asIScriptContext * pCtx,int aArgIndex, float& aArg)
    {
        pCtx->SetArgFloat(aArgIndex, aArg);
    }
    template<>
    static    void                SetArg<double>(asIScriptContext * pCtx,int aArgIndex, double& aArg)
    {
        pCtx->SetArgDouble(aArgIndex, aArg);
    }
    template<>
    static    void                SetArg<void *>(asIScriptContext * pCtx,int aArgIndex, void *& aArg)
    {
        pCtx->SetArgObject(aArgIndex, aArg);
    }

    static asIScriptEngine *    sm_pEngine;
};

class AngelObject
{
public:
    AngelObject()
    {
        refCount = 1;
    }
    void AddRef()
    {
        refCount ++;
    }
    void Release()
    {
        refCount --;
    }

protected:
    mutable int refCount;
};


#define EXPORTGLOBALFUNCTION(X) AlHelper::RegisterGlobalFunction(TemplateFunc::GetFuncDeclarationString4AL(X,#X).c_str(), asFUNCTION(X), asCALL_CDECL);

#endif